package nodes

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"time"
)

var HTTPTransport = &http.Transport{
	DialContext: (&net.Dialer{
		Timeout:   5 * time.Second,  // 连接超时时间
		KeepAlive: 10 * time.Second, // 保持长连接的时间
	}).DialContext, // 设置连接的参数
	IdleConnTimeout:       60 * time.Second, // 空闲连接的超时时间
	ExpectContinueTimeout: 30 * time.Second, // 等待服务第一个响应的超时时间
	MaxConnsPerHost:       1,                // 每个host保持的最大连接数
}

// HttpGetter implements interface NodeGetter, as a client,
// can get key's data from its brother server
// baseAddr is a remote server's base url, such as http://example.com/distributedcache/
type HttpHandler struct {
	baseAddr string
	client   *http.Client
}

func NewHttpHandler(baseAddr string) *HttpHandler {
	h := &HttpHandler{
		baseAddr: baseAddr,
		client:   &http.Client{Transport: HTTPTransport},
	}

	return h
}

func (h *HttpHandler) SetGroup(group string) error {
	fullURL := fmt.Sprintf("http://%v/%v/%v", h.baseAddr, url.QueryEscape("Groups"), url.QueryEscape(group))
	res, err := http.Post(fullURL, "", nil)
	if err != nil {
		return err
	}

	temp := make(map[string]interface{})
	// read http response body
	data, err1 := io.ReadAll(res.Body)
	if err1 != nil {
		return err1
	}

	// unmashal body data into temp
	if err1 := json.Unmarshal(data, &temp); err1 != nil {
		return err1
	}

	// status code is not 200
	if res.StatusCode != http.StatusOK {
		return temp["err"].(error)
	}

	fmt.Println("setGroup by http")
	return nil
}

func (h *HttpHandler) DeleteGroup(group string) error {
	fullURL := fmt.Sprintf("http://%v/%v/%v", h.baseAddr, url.QueryEscape("Groups"), url.QueryEscape(group))
	// call api custom DELETE
	req, _ := http.NewRequest("DELETE", fullURL, nil)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	temp := make(map[string]interface{})
	// read http response body
	data, err1 := io.ReadAll(res.Body)
	if err1 != nil {
		return err1
	}

	// unmashal body data into temp
	if err1 := json.Unmarshal(data, &temp); err1 != nil {
		return err1
	}

	// status code is OK
	if res.StatusCode == http.StatusOK {
		fmt.Println("delete Group by http")
		return nil
	}

	// status code is not OK
	return temp["err"].(error)
}

// Get gets data from remote server, by using http.Get()
func (h *HttpHandler) Get(group string, key string) ([]byte, error) {
	fullURL := fmt.Sprintf("http://%v/%v/%v/%v",
		h.baseAddr, url.QueryEscape("keys"), url.QueryEscape(group), url.QueryEscape(key),
	)
	res, err := http.Get(fullURL)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	temp := make(map[string]interface{})
	data, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response body: %v", err)
	}

	// unmashal body data into temp
	if err1 := json.Unmarshal(data, &temp); err1 != nil {
		return nil, err1
	}

	// status code is OK
	if res.StatusCode == http.StatusOK {
		fmt.Println("get by http")
		return []byte(temp["value"].(string)), nil
	}

	return nil, errors.New(temp["err"].(string))
}

// Set gets data from remote server, by using http.Get()
// This function is not correct, need modify later
func (h *HttpHandler) Set(group string, key string, value []byte) error {
	fullURL := fmt.Sprintf("http://%v/%v/%v/%v/%v",
		h.baseAddr, url.QueryEscape("keys"), url.QueryEscape(group), url.QueryEscape(key), url.QueryEscape(string(value)),
	)
	if _, err := http.Post(fullURL, "", nil); err != nil {
		return err
	}

	fmt.Println("set by http")
	return nil
}

// Delete gets data from remote server, by using http.Get()
func (h *HttpHandler) Delete(group string, key string) error {
	fullURL := fmt.Sprintf("http://%v/%v/%v/%v",
		h.baseAddr, url.QueryEscape("keys"), url.QueryEscape(group), url.QueryEscape(key),
	)
	req, _ := http.NewRequest("DELETE", fullURL, nil)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	temp := make(map[string]interface{})
	// read http response body
	data, err1 := io.ReadAll(res.Body)
	if err1 != nil {
		return err1
	}

	// unmashal body data into temp
	if err1 := json.Unmarshal(data, &temp); err1 != nil {
		return err1
	}

	// status code is OK
	if res.StatusCode == http.StatusOK {
		fmt.Println("delete by http")
		return nil
	}

	// status code is not OK
	return temp["err"].(error)
}

func (h *HttpHandler) GetBaseAddr() string {
	return h.baseAddr
}
